﻿#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <assert.h>
using namespace std;
#include "List Class.h"

//什么是左值？什么是左值引用？
//左值是一个表示数据的表达式(如变量名或解引用的指针)，我们可以获取它的地址 + 可以对它赋值，
//左值可以出现赋值符号的左边，右值不能出现在赋值符号左边。
//定义时const修饰符后的左值，不能给他赋值，但是可以取它的地址。
//左值引用就是给左值的引用，给左值取别名。
//int main() {
//	// 以下的p、b、c、*p都是左值
//	int* p = new int(0);
//	int b = 1;
//	const int c = 2;
//	// 以下几个是对上面左值的左值引用
//	int*& rp = p;
//	int& rb = b;
//	const int& rc = c;
//	int& pvalue = *p;
//	return 0;
//}


//什么是右值？什么是右值引用？
//右值也是一个表示数据的表达式，如：字面常量、表达式返回值，函数返回值
// (这个不能是左值引用返回【如果函数返回的是一个右值（例如返回临时对象或值类型），
// 则不能通过左值引用（T&）返回】，因为右值无法绑定到非 const 的左值引用。])等等，
//右值可以出现在赋值符号的右边，但是不能出现出现在赋值符号的左边，右值不能取地址。
//右值引用就是对右值的引用，给右值取别名。
//int main() {
//	double x = 1.1, y = 2.2;
//	// 以下几个都是常见的右值
//	10;
//	x + y;
//	fmin(x, y);
//	// 以下几个都是对右值的右值引用
//	int&& rr1 = 10;
//	double&& rr2 = x + y;
//	double&& rr3 = fmin(x, y);
//	// 这里编译会报错：error C2106: “=”: 左操作数必须为左值
//	//10 = 1;
//	//x + y = 1;
//	//fmin(x, y) = 1;
//
//	//需要注意的是右值是不能取地址的，但是给右值取别名后，
//	//会导致右值被存储到特定位置，且可以取到该位置的地址，
//	//也就是说例如：不能取字面量10的地址，但是rr1引用后，可以对rr1取地址，也可以修改rr1。
//	//如果不想rr1被修改，可以用const int&& rr1 去引用，是不是感觉很神奇，
//	//这个了解一下实际中右值引用的使用场景并不在于此，这个特性也不重要。
//	return 0;
//}



//int main() {
//	//左值引用能否给右值取别名
//	//不能，但const左值引用可以
//	const int& r1 = 10;
//	int i = 0;
//	int j = i;
//	const int& r2 = i + j;
//
//	//右值引用给右值取别名
//	int&& rr1 = 10;
//	int&& rr2 = i + j;
//	//右值引用能否给左值取别名
//	//不能，但右值引用可以给move(左值)取别名
//	int&& rr3 = move(i);
//	return 0;
//}
//左值是可以取地址的值(非const可以修改，const不可以修改)
//左值引用可以给左移取别名
//左值引用不能给右值取别名，但const左值引用可以给右值取别名

//右值是不能取地址的值
//右值引用可以给右值取别名
//右值引用不能给左值取别名，但可以给move(左值)取别名

//左值引用使用场景
//引用传参
//引用返回(部分场景适用：除了函数作用域，返回对象生命周期还没结束)



namespace bit {
	class string {
	public:
		typedef char* iterator;
		iterator begin() { return _str; }
		iterator end() { return _str + _size; }
		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size) {
			//cout << "string(char* str)" << endl;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		// s1.swap(s2)
		void swap(string& s) {
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}
		// 拷贝构造
		string(const string& s) { //const左值引用既可以引用左值，也可以引用右值
			cout << "string(const string& s) -- 拷贝构造 深拷贝" << endl;
			string tmp(s._str);
			swap(tmp);
		}

		//C++的右值分2类：
		//1.纯右值：内置类型(纯粹的字面值、临时对象或表达式计算结果，没有持久的内存地址（不能取地址）)
		//2.将亡值：自定义类型(即将被移动（资源被转移）的对象，通常是“生命周期即将结束”的值。)
		//         有内存地址（可以取地址），但允许被“掏空”（资源被移动）。

		//移动构造
		string(string&& s) {
			//to_string 中的 ret 是即将销毁的局部变量
			//编译器将其视为右值（即使它是左值名称，但处于"将亡值"场景）
			//构造临时对象：需要用一个右值（ret）构造临时对象
			//移动构造函数内部：新对象（临时对象）刚创建，它的成员是默认值。
			//参数 s 是 ret 的别名（引用），持有 ret 的真实数据
			//调用全局的 swap 函数交换每个成员
			//函数结束后：ret（即移动后的 s）被销毁
			//临时对象现在持有原 ret 的资源（"1234"），没有发生深拷贝
			cout << "string(string&& s) -- 移动构造" << endl;
			swap(s);//this：指向正在构造的临时对象（内存由编译器分配）
			//临时对象不新开空间
		}

		// 赋值重载
		string& operator=(const string& s) {
			cout << "string& operator=(string s) -- 赋值重载 深拷贝" << endl;
			//string tmp(s);
			//swap(tmp);
			if (this != &s) {
				char* tmp = new char[s._capacity + 1];
				strcpy(tmp, s._str);
				delete[] _str;
				_str = tmp;
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}
		string& operator=(string&& s) {
			//ret 的资源（"1234"）已通过 swap 转移给临时对象。
			//参数 s：就是刚才那个临时对象（右值引用绑定）。 
			// this：指向 main 中的str。str和s交换资源。
			//临时对象s带着str的资源被销毁，并释放
			cout << "string& operator=(string&& s) -- 移动赋值" << endl;
			swap(s);
			return *this;
		}

		~string() {
			delete[] _str;
			_str = nullptr;
		}
		char& operator[](size_t pos) {
			assert(pos < _size);
			return _str[pos];
		}
		void reserve(size_t n) {
			if (n > _capacity) {
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
		void push_back(char ch) {
			if (_size >= _capacity) {
				size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newcapacity);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}
		//string operator+=(char ch)
		string& operator+=(char ch) {
			push_back(ch);
			return *this;
		}
		const char* c_str() const { return _str; }
	private:
		char* _str = nullptr;
		size_t _size = 0;
		size_t _capacity = 0; // 不包含最后做标识的\0
	};

	bit::string to_string(int x) {
		bit::string ret;
		while (x) {
			int val = x % 10;
			x /= 10;
			ret += ('0' + val);
		}
		reverse(ret.begin(), ret.end());
		return ret;
		//该场景不能使用左值引用返回，ret出了作用域要销毁
		//也不能使用右值引用返回，右值引用不能引用左值
		//ret拷贝构造临时对象，在赋值给外面的str，发生两次深拷贝
		//ret开辟空间存1234，然后拷贝构造给临时对象，临时对象也开辟空间存1234，
		//出了函数作用域ret销毁并释放空间，当ret赋值给外面的str并走到下一行，s销毁并释放空间

		//ret 是局部变量（左值），但 return 时会隐式转换为将亡值（xvalue）。
		//调用string(string && s)，s 就是 ret 转换成的将亡值。
		//移动构造后，ret 的资源被转移，外部得到新对象。
	}
}

//int main() {
//	//无移动构造和移动赋值：
//	//这里有深拷贝发生，to_string函数中要拷贝构造临时对象
//	//再用临时对象赋值给s
//	
//	//有移动构造和移动赋值
//	//整个移动构造和移动复制的过程中只有ret开辟了空间存储资源，临时对象把ret的换走了，
//	//然后main函数中的str再把临时对象的换走了，也就相当于间接换走了ret的资源。
//	bit::string str;	
//	str = bit::to_string(1234);
//	
//	//------------------------------------------------------------------
//	//如果没有移动构造，下面这种写法还是会发生一次深拷贝，相当于ret拷贝构造str。
//	//有了移动构造就是ret和str交换资源
//	bit::string str = bit::to_string(1234);
//	
//	//-------------------------------------------------------------------
//	//下面这里将s1转为右值，虽然不调用拷贝构造(调用移动构造)
//	//但资源交换后，s1变为空了
//	bit::string s1("hello world");
//	bit::string s2 = s1;//这里调用拷贝构造
//	bit::string s3 = move(s1);
//	
//	//-------------------------------------------------------------------
//	//move不会改变s1的属性，只是move的返回值是右值
//	move(s1);
//	bit::string s3 = s1;//调用的是拷贝构造
//	
//	return 0;
//	//右值引用的移动语义主要针对深拷贝，浅拷贝意义不大
//
//	//左值引用（T&）只能绑定到有名字的、持久化的对象（左值），但无法绑定到：
//	//临时对象（右值）：如函数返回值、表达式结果等。
//	//需要被“移动”而非拷贝的资源：比如动态分配的数组、文件句柄等。
//	
//	//右值引用的核心作用
//	//右值引用（T&& ）直接绑定到临时对象（右值），并允许“窃取”其资源，避免深拷贝。典型场景包括：
//	//函数返回临时对象：如std::vector<int> createVector()。
//	//临时对象赋值或传参：如a = createVector()。
//	//标准库容器的移动语义：如std::vector的移动构造函数。
//}



//STL容器插入接口函数也增加了右值引用版本
//int main() {
//	list<bit::string> lt;
//	bit::string s1("hello world");
//	lt.push_back(s1);//左值引用插入，拷贝构造 深拷贝
//	
//	lt.push_back(bit::to_string(1234));
//	//调用2次移动构造。
//	//当bit::to_string(1234)执行完毕后：
//	//它返回一个临时对象（右值）ret。
//	//由于 ret 是一个局部变量，在函数返回时，它会被视为即将销毁的右值。
//	//此时，编译器会优先调用 移动构造函数（string(string && s)）来构造返回值，而不是拷贝构造。
//	//第二次移动构造：push_back 接收临时对象
//	//push_back 接收到 bit::to_string(1234) 返回的临时对象（右值）。
//	//STL 的 list 在插入元素时，会构造一个新的节点来存储该字符串。
//	//由于传入的是 右值，list 内部会调用 string 的 移动构造函数 来构造新节点中的string对象。
//
//	lt.push_back("11111");
//	//只调用一次移动构造
//	//lt.push_back("11111"); 直接传入 const char*，
//	//只需一次隐式构造 + 一次移动构造，没有额外的返回值传递过程。
//
//	//右值引用（&& ）不仅优化了函数返回值（如 return 临时对象），
//	//还优化了参数传递（如函数传参或容器插入操作），
//	//特别是对于需要深拷贝的类（如 string、vector 等），
//	//它可以避免不必要的深拷贝，直接“窃取”资源，提高性能。
//	return 0;
//}


//验证自己实现的list
//int main() {
//	bit::list<bit::string> lt;
//	bit::string s1("hello world");//拷贝构造 深拷贝
//	lt.push_back(s1);//拷贝构造 深拷贝
//
//	lt.push_back(bit::to_string(1234));
//	//具体发生过程：拷贝构造 深拷贝 -> 移动构造 ->拷贝构造 深拷贝
//
//	//bit::to_string(1234)返回值是右值，所以传给了push_back的右值引用版本，
//	//但是右值被右值引用 引用后的属性是左值。
//	//因为如果不这样，移动构造和移动赋值里的临时对象就无法修改，也就无法实现交换。
//	//即右值不能被直接修改，但右值被右值引用后需要被修改。
//
//	lt.push_back("11111");
//	return 0;
//}



// 模板中的&&不代表右值引用，而是万能引用，其既能接收左值又能接收右值。
// 模板的万能引用只是提供了能够同时接收左值引用和右值引用的能力，
// 但是引用类型的唯一作用就是限制了接收的类型，后续使用中都退化成了左值
//“引用类型的唯一作用就是限制了接收的类型”的意思是：
//在函数模板参数中使用的引用（无论是左值引用&还是右值引用&&），
//其主要作用就是限制函数可以接受的参数类型，而不会改变参数在函数内部使用时的值类别。

void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }
template<typename T>
void PerfectForward(T&& t) {
	//Fun(t);//都退化为左值
	//Fun(move(t));//这种方式会导致全部变成右值引用或const 右值引用

	//如果期望实参的属性与形参一致，使用完美转发
	Fun(forward<T>(t));
}

//完美转发不能再非模板使用，因为只有在模板中&&才代表万能引用，
//这里是右值引用，不能传左值
void PerfectForward(int&& t) {
	Fun(forward<int>(t));
}
int main() {
	PerfectForward(10);// 右值
	int a;
	PerfectForward(a);//左值
	PerfectForward(std::move(a)); // 右值
	const int b = 8;
	PerfectForward(b);// const 左值
	PerfectForward(std::move(b)); // const 右值
	//虽然上面传的值有左值和右值，但调用后全部变成左值引用或const 左值引用
	//无论参数是以什么引用形式传入的（左值引用或右值引用），在函数内部使用时，这个参数名本身都是一个左值
	//也就是说，即使你传入的是一个右值引用，在函数内部使用这个参数名时，它仍然是一个左值
	//原因：C++不自动将具名变量(任何有名字的变量或参数)当作右值的核心原因：避免意外的资源窃取导致后续代码出错。
	return 0;
}